/***
 * Windows Tray Icon
 * -----------------
 *
 * Written by Jan Struyf
 *
 *  jan.struyf@cs.kuleuven.ac.be
 *  http://jeans.studentenweb.org/java/trayicon/trayicon.html
 *
 * Please mail me if you
 *	- 've found bugs
 *	- like this program
 *	- don't like a particular feature
 *	- would like something to be modified
 *
 * I always give it my best shot to make a program useful and solid, but
 * remeber that there is absolutely no warranty for using this program as
 * stated in the following terms:
 *
 * THERE IS NO WARRANTY FOR THIS PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
 * LAW. THE COPYRIGHT HOLDER AND/OR OTHER PARTIES WHO MAY HAVE MODIFIED THE
 * PROGRAM, PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 * TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 * PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 * REPAIR OR CORRECTION.
 *
 * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL ANY COPYRIGHT HOLDER,
 * OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM,
 * BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
 * PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
 * INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
 * PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
 * PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * May the Force be with you... Just compile it & use it!
 */

package demo.swing;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
import javax.swing.border.*;

import com.jeans.trayicon.*;

// Demo app for Java Tray Icon
public class SwingTrayIcon extends JFrame {    

	public final static int ENABLE_ITEM = 0;
	public final static int BOLD_ITEM = 1;
	public final static int CHECK_ITEM = 2;

	protected TrayIconButton[] m_Icon;
	protected JCheckBox m_SwingMenu, m_UseChinese;
	protected JButton m_ExitButton, m_HideButton, m_AboutButton;	
	protected SwingTrayPopup[] m_Popup = new SwingTrayPopup[4];
	protected BalloonBox m_Balloon;
	// protected Font m_Font = new Font("MS Song", Font.PLAIN, 12);

	public SwingTrayIcon() throws TrayIconException, InterruptedException {
		super("SwingTrayIcon");

		// Set callback method to send windows messages through Tray Icon library
		// (see WindowsMessageCallback)
		WindowsTrayIcon.setWindowsMessageCallback(new WindowsMessageCallback());

		// Create the icon
		JPanel panel = new JPanel();
		panel.setLayout(new GridLayout(0,1,3,3));		
		setIconImage(loadImage("Duke16.gif"));

		// Create 4 checkboxes with icons: Printer, Battery, France & MS-DOS
		m_Icon = new TrayIconButton[4];
		panel.add(m_Icon[0] = new TrayIconButton("Duke",loadImage("Duke16.gif"),16,16,this));
		panel.add(m_Icon[1] = new TrayIconButton("Printer",loadImage("Printer.gif"),16,16,this));
		panel.add(m_Icon[2] = new TrayIconButton("MS-DOS",loadImage("MsDos.gif"),16,16,this));
		panel.add(m_Icon[3] = new TrayIconButton("France",loadImage("France.gif"),16,16,this));		

		// Tray Icon left mouse button event restores the main window
		// (make it visible again/requests focus)
		RestoreListener listener = new RestoreListener(false);
		TestMouseListener mouse = new TestMouseListener();
		BalloonMessageListener balloon = new BalloonMessageListener();
		for (int k = 0; k < 4; k++) {
			m_Icon[k].addActionListener(listener);
			m_Icon[k].addMouseListener(mouse);
			m_Icon[k].addBalloonListener(balloon);
		}
		
		// Each icon has it's own popup menu
		// Because the menu's contain state information, you can't use the same menu for different icons)
		m_Icon[0].setPopup(makePopup());
		m_Icon[1].setPopup(makePopup());
		m_Icon[2].setPopup(makePopup());
		m_Icon[3].setPopup(makePopup());
		m_Icon[0].selectIcon(true);

		// Panel with exit, hide & about button - With hide you can hide the main window and restore it
		// by clicking on one of the Tray Icons
		JCheckBox m_SwingMenu = new JCheckBox("Use Swing menu");
		m_SwingMenu.addItemListener(new MySwingMenuListener());
		panel.add(m_SwingMenu);				
		//m_UseChinese = new JCheckBox("Display menu in Chinese");
		//m_UseChinese.addItemListener(new MySwingMenuListener());
		//panel.add(m_UseChinese);
				
		JPanel buttons = new JPanel();
		buttons.setLayout(new GridLayout(1,0,3,3));
		buttons.add(m_ExitButton = new JButton("Exit"));
		m_ExitButton.addActionListener(new ExitListener());
		buttons.add(m_HideButton = new JButton("Hide"));
		m_HideButton.addActionListener(new HideListener());
		buttons.add(m_AboutButton = new JButton("About"));
		m_AboutButton.addActionListener(new AboutListener());
		panel.add(buttons);
		panel.setBorder(new EmptyBorder(3,3,3,3));
		setContentPane(panel);
		addWindowListener(new WindowClosingListener());
		pack();
	}
	
	// Return one of the icons
	public WindowsTrayIcon getIcon(int i) {
		return m_Icon[i].getIcon();
	}
	
	// Return one of the icon's images
	public Image getIconImage(int i) {
		return m_Icon[i].getImage();
	}
	
	// Update checkboxes on balloon control
	public void updateBalloonButtons() {
		if (m_Balloon != null) m_Balloon.updateButtons();
	}
	
	// Create the popup menu for each Tray Icon (on right mouse click)
	public TrayIconPopup makePopup() {
		// Make new popup menu
		TrayIconPopup popup = new TrayIconPopup();		
		// Add show, about, submenu, separator & exit item
		TrayIconPopupSimpleItem item = new TrayIconPopupSimpleItem("&Show");
		item.setDefault(true);		
		// Each menu item can have it's own ActionListener		
		item.addActionListener(new RestoreListener(true));
		popup.addMenuItem(item);
		item = new TrayIconPopupSimpleItem("&About");
		item.addActionListener(new AboutListener());
		popup.addMenuItem(item);		
		item = new TrayIconPopupSimpleItem("&Balloon");				
		item.addActionListener(new BalloonListener());
		popup.addMenuItem(item);		
		item = new TrayIconPopupSimpleItem("&Flash window");				
		item.addActionListener(new FlashListener());
		popup.addMenuItem(item);		
		// Create a submenu with title enable and items check 1 & check 2
		TrayIconPopup sub = new TrayIconPopup("&Options");		
		// Create and add some checkbox menu items
		TrayIconPopupCheckItem test = new TrayIconPopupCheckItem("Test");
		sub.addMenuItem(test);
		sub.addMenuItem(new TrayIconPopupSeparator());
		TrayIconPopupCheckItem ena = new TrayIconPopupCheckItem("Enable Test");
		ena.addActionListener(new ChangeMenuListener(test, ENABLE_ITEM));			
		ena.setCheck(true);
		sub.addMenuItem(ena);
		TrayIconPopupCheckItem bold = new TrayIconPopupCheckItem("Bold Test");
		bold.addActionListener(new ChangeMenuListener(test, BOLD_ITEM));
		sub.addMenuItem(bold);
		TrayIconPopupCheckItem check = new TrayIconPopupCheckItem("Check Test");
		check.addActionListener(new ChangeMenuListener(test, CHECK_ITEM));
		test.addActionListener(new ChangeMenuListener(check, CHECK_ITEM));
		sub.addMenuItem(check);		
		// Add submenu to the main menu
		popup.addMenuItem(sub);
		// Add a separator		
		popup.addMenuItem(new TrayIconPopupSeparator());
		// Add exit item
		item = new TrayIconPopupSimpleItem("E&xit");
		item.addActionListener(new ExitListener());
		popup.addMenuItem(item);
		return popup;
	}
	
	public SwingTrayPopup makeSwingPopup() {
		SwingTrayPopup popup = new SwingTrayPopup();
		// popup.setFont(m_Font);
		JMenuItem item = new JMenuItem(TR.get(TR.SHOW), new ImageIcon(loadImage("Duke16.gif")));				
		// Each menu item can have it's own ActionListener		
		item.addActionListener(new RestoreListener(true));
		// item.setFont(m_Font);
		popup.add(item);
		item = new JMenuItem(TR.get(TR.ABOUT), new ImageIcon(loadImage("About.gif")));	
		item.addActionListener(new AboutListener());
		// item.setFont(m_Font);
		popup.add(item);
		item = new JMenuItem(TR.get(TR.BALLOON), new ImageIcon(loadImage("Balloon.gif")));	
		item.addActionListener(new BalloonListener());
		popup.add(item);				
		item = new JMenuItem(TR.get(TR.FLASH), new ImageIcon(loadImage("Battery.gif")));	
		item.addActionListener(new FlashListener());
		popup.add(item);		
		// Create a submenu with title enable and items check 1 & check 2
		JMenu sub = new JMenu(TR.get(TR.OPTIONS));
		// sub.setFont(m_Font);
		// Create and add some checkbox menu items
		JCheckBoxMenuItem test = new JCheckBoxMenuItem(TR.get(TR.TEST));
		test.setEnabled(false);
		// test.setFont(m_Font);
		sub.add(test);
		sub.addSeparator();
		JCheckBoxMenuItem ena = new JCheckBoxMenuItem(TR.get(TR.EN_TEST));
		ena.addItemListener(new SwingChangeMListener(test, ENABLE_ITEM));
		ena.setState(true);
		// ena.setFont(m_Font);
		sub.add(ena);
		JCheckBoxMenuItem bold = new JCheckBoxMenuItem(TR.get(TR.BL_TEST));
		bold.addItemListener(new SwingChangeMListener(test, BOLD_ITEM));		
		bold.setEnabled(false);
		// bold.setFont(m_Font);
		sub.add(bold);
		JCheckBoxMenuItem check = new JCheckBoxMenuItem(TR.get(TR.CH_TEST));
		check.addItemListener(new SwingChangeMListener(test, CHECK_ITEM));		
		test.addItemListener(new SwingChangeMListener(check, CHECK_ITEM));		
		// check.setFont(m_Font);
		sub.add(check);		
		// Add submenu to the main menu
		popup.add(sub);
		// Add a separator		
		popup.addSeparator();
		// Add exit item
		item = new JMenuItem(TR.get(TR.EXIT));
		item.addActionListener(new ExitListener());
		// item.setFont(m_Font);
		popup.add(item);
		return popup;	        
	}
		
	// Load a gif image (used for loading the 16x16 icon gifs)
	public static Image loadImage(String fileName) {
		return Toolkit.getDefaultToolkit().getImage("demo"+File.separator+"images"+File.separator+fileName);
	}

	// Use native windows look & feel
	public static void setLookAndFeel() {
		try {
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		} catch (Exception ex) {}
	}    	

	// Main proc
	public static void main(String[] args) {	    
		try {
			setLookAndFeel();
			TR.load("demo"+File.separator+"swing"+File.separator+"menu.eng");
			String appName = "SwingTray";

			// First check if there's another instance of our app running by sending a windows
			// message to a hidden icon window "TestTray" - each Tray Icon app has a hidden window
			// that receives the mouse/menu messages for it's Tray Icons
			long result = WindowsTrayIcon.sendWindowsMessage(appName, 1234);
			if (result != -1) {
				// If window exists, there's already an instance of our app running
				// Print message and exit (other app will restore its window when receiving
				// our message - see WindowsMessageCallback
				System.out.println("[Already running other instance of "+appName+" (returns: "+result+")].");
				return;
			}
			// Init the Tray Icon library given the name for the hidden window
			WindowsTrayIcon.initTrayIcon(appName);
			// Show the icon
			SwingTrayIcon tray = new SwingTrayIcon();
			SwingTrayIcon.centerDialog(tray);
			tray.setVisible(true);
		} catch (TrayIconException e) {
			System.out.println("Error: "+e.getMessage());
		} catch (IOException e) {
			System.out.println("Error: "+e.getMessage());
		} catch (InterruptedException e) { }
	}
	
	// Called when app exits
	public void doExit() {
		System.out.println("[Exit selected / Close requested].");
		
		// Free all Tray Icon resources - always call this on exit
		WindowsTrayIcon.cleanUp();
		
		// Exit application
		System.exit(0);
	}


	// Called when app must be hidden
	public void doHide(boolean exitOnFail) {
		System.out.println("[Hide selected].");

		// Check if there's a Tray Icon visible
		// Hide works only when there's an icon in the system tray
		boolean visible = true;
		for (int k = 0; k < 4; k++)  {
			if (m_Icon[k].testVisible()) visible = false;
		}
		setVisible(visible);
		if (visible == true) {
			System.out.println("[Hide works only when there's an icon in the system tray].");
			if (exitOnFail) doExit();
		}
	}
    
	// Select a different type of menu (AWT or Swing)
	public void doUpdateMenu(boolean awtSwing, boolean engZh) {
		if (awtSwing) {            
			for (int i = 0; i < 4; i++) {
				m_Icon[i].setPopup(makePopup());
				m_Popup[i].setTrayIcon(null);
			}            
		} else {            
			for (int i = 0; i < 4; i++) {
				m_Icon[i].setPopup(null);
				if (m_Popup[i] == null) m_Popup[i] = makeSwingPopup();
				m_Icon[i].setSwingPopup(m_Popup[i]);
			}
		}
	}
        
	// Center a dialog on screen
	public static void centerDialog(Window frame) {
		Dimension dialogSize = frame.getSize();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation(screenSize.width/2 - dialogSize.width/2,
		                  screenSize.height/2 - dialogSize.height/2);
	}        

	// Callback listener handles incoming windows messages. In this demo, a windows message is sent
	// when the user tries to start a second instance of the demo app. You can try this one by opening
	// two MS-DOS prompts and say in each one "java demo.swing.SwingTrayIcon"
	// MS-DOS 1:
	//      C:\TrayIcon>java demo.swing.SwingTrayIcon
	//      ...
	//      Other instance started (parameter: 1234)
	//
	// MS-DOS 2:
	//      C:\TrayIcon>java demo.swing.SwingTrayIcon
	//      Already running other instance of TestTray (returns: 4321)
	private class WindowsMessageCallback implements TrayIconCallback {

		public int callback(int param) {
			// Param contains the integer value send with sendWindowsMessage(appName,param)
			System.out.println("[Other instance started (parameter: "+param+")].");
			setVisible(true); toFront(); requestFocus();
			
			// Return integer value to other process
			return 4321;
		}
	}
	
	// Callback listener handles exit button / exit popup menu
	private class ExitListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			doExit();
		}
	}
	
	// Called when close button in window title bar is selected
	public class WindowClosingListener extends WindowAdapter {
	
		public void windowClosing(WindowEvent e) {
			doHide(true);
		}
	}
		
	// Callback listener for hide button
	private class HideListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			doHide(false);			
		}
	}

	// Callback listener handles about button
	private class AboutListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			System.out.println("[About selected].");
			// TestTrayIcon.this instead of this$0 for Java 1.3 compatibility
			AboutBox box = new AboutBox(SwingTrayIcon.this);
			centerDialog(box);
			box.setVisible(true);
		}
	}	
	
	// Callback listener handles balloon button
	private class BalloonListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			System.out.println("[Balloon selected].");
			// TestTrayIcon.this instead of this$0 for Java 1.3 compatibility
		 	if (m_Balloon == null) {
		 		m_Balloon = new BalloonBox(SwingTrayIcon.this);
		 		centerDialog(m_Balloon);
		 	}
		 	m_Balloon.updateButtons();
			m_Balloon.setVisible(true);
		}
	}		
	
	// Callback listener handles balloon button
	private class FlashListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			System.out.println("[Flash window].");
			try {
				WindowsTrayIcon.flashWindow(SwingTrayIcon.this);
			} catch (TrayIconException e) {
				System.out.println("Flash window not supprted.");
				System.out.println("Are you running windows 95?");
			}
		}
	}			

	// Callback listener handles balloon messages
	private class BalloonMessageListener implements TrayBalloonListener {

		public void balloonChanged(TrayBalloonEvent evt) {
			if ((evt.getMask() & TrayBalloonEvent.SHOW) != 0) {
				System.out.println("Balloon shown");
			}
			if ((evt.getMask() & TrayBalloonEvent.HIDE) != 0) {
				System.out.println("Balloon hidden");
			}
			if ((evt.getMask() & TrayBalloonEvent.TIMEOUT) != 0) {
				System.out.println("Balloon time out");
			}
			if ((evt.getMask() & TrayBalloonEvent.CLICK) != 0) {
				System.out.println("Balloon clicked by user");
			}
		}
	}
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class RestoreListener implements ActionListener {
		
		protected boolean from_menu;
	
		public RestoreListener(boolean fromMenu) {
			from_menu = fromMenu;
		}

		public void actionPerformed(ActionEvent evt) {
			if (from_menu) {				
				setVisible(true);
				toFront(); requestFocus();
			} else if (evt.getActionCommand().equals("Left")) {
				if (!isVisible()) {
					// Make main window visible if it was hidden
					setVisible(true);
					// Request input focus
					toFront(); requestFocus();
				} else {
					doHide(false);
				}
			}
		}
	}
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class ChangeMenuListener implements ActionListener {

		protected TrayIconPopupCheckItem m_Item;
		protected int m_Change;

		public ChangeMenuListener(TrayIconPopupCheckItem item, int change) {
			m_Item = item;
			m_Change = change;
		}

		public void actionPerformed(ActionEvent evt) {
			TrayIconPopupCheckItem source = (TrayIconPopupCheckItem)evt.getSource();
			boolean value = source.getCheck();
			switch (m_Change) {
			    case ENABLE_ITEM:
			        m_Item.setEnabled(value); break;
			    case BOLD_ITEM:
			        m_Item.setDefault(value); break;
			    case CHECK_ITEM:
			        m_Item.setCheck(value); break;
			}
		}
	}	
	
	// Callback listener handles restore (click left on any icon / show popup menu)
	private class SwingChangeMListener implements ItemListener {

		protected JCheckBoxMenuItem m_Item;
		protected int m_Change;

		public SwingChangeMListener(JCheckBoxMenuItem item, int change) {
			m_Item = item;
			m_Change = change;
		}

		public void itemStateChanged(ItemEvent e) {
			boolean value = e.getStateChange() == ItemEvent.SELECTED;
			switch (m_Change) {
			    case ENABLE_ITEM:
			        m_Item.setEnabled(value); break;
/*			    case BOLD_ITEM:
			        m_Item.setDefault(value); break;*/
			    case CHECK_ITEM:
			        m_Item.setState(value); break;
			}
		}
	}	
	
	// Called when user selects "Use Swing Menu" checkbox
	public class MySwingMenuListener implements ItemListener {
        
		public void itemStateChanged(ItemEvent e) {
			boolean chinese = false; // m_UseChinese.getState();
			if (e.getStateChange() == ItemEvent.SELECTED) {
				doUpdateMenu(false, !chinese);
			} else {                
				doUpdateMenu(true, !chinese);                
			}
		}    
	}
    
	// Test listener for double-click events
	public class TestMouseListener extends MouseAdapter {
    
		public void mousePressed(MouseEvent evt) {
			if ((evt.getModifiers() & MouseEvent.BUTTON1_MASK) != 0 && evt.getClickCount() == 2) {
				System.out.println("[Tray icon double clicked].");
			}
		}
	}
}

// Stupid about box for demo app
class AboutBox extends JDialog {

	// Create new about box given parent frame
	public AboutBox(JFrame parent)  {
		// Make modal dialog given parent and title
		super(parent, "About TrayIcon", true);
		// Layout stuff
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout(3,3));
		JPanel txt = new JPanel();
		txt.setLayout(new GridLayout(0,1));
		txt.add(new JLabel("TrayIcon version: "+WindowsTrayIcon.TRAY_VERSION));
		txt.add(new JLabel("OS version: "+WindowsTrayIcon.getWindowsVersionString()));
		txt.add(new JLabel("Written by Jan Struyf <Jan.Struyf@cs.kuleuven.ac.be>"));		
		panel.add(txt, BorderLayout.CENTER);
		JPanel buttons = new JPanel();
		buttons.setLayout(new FlowLayout());
		JButton button = new JButton("OK");
		buttons.add(button, BorderLayout.CENTER);
		panel.add(buttons, BorderLayout.SOUTH);
		// Close listeners for OK button and window button
		button.addActionListener(new CloseListener());
		addWindowListener(new CloseWindowListener());
		panel.setBorder(new EmptyBorder(4,4,4,4));
		setContentPane(panel);
		pack();
	}

	// Close listener for OK button
	private class CloseListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			dispose();
		}
	}

	// Close listener for windows button
	private class CloseWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent evt) {
			dispose();
		}
	}
}

// Stupid about box for demo app
class BalloonBox extends JFrame {

	SwingTrayIcon m_Parent;
	JRadioButton m_Duke, m_Printer, m_MSDOS, m_France;
	JRadioButton m_Info, m_Warning, m_Error, m_None;
	JTextField m_Title, m_Message, m_Time;
	JCheckBox m_Sound, m_Unicode;	

	// Create new about box given parent frame
	public BalloonBox(SwingTrayIcon parent)  {
		// Make modal dialog given parent and title
		super("TrayIcon Balloon");
		m_Parent = parent;
		// Layout stuff
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout(3,3));		
		JPanel select = new JPanel();
		select.setLayout(new BorderLayout(3,3));		
		JPanel p1 = new JPanel();		
		ButtonGroup g1 = new ButtonGroup();
		p1.setLayout(new GridLayout(0,1));
		p1.add(m_Duke = new JRadioButton("Duke"));
		p1.add(m_Printer = new JRadioButton("Printer"));
		p1.add(m_MSDOS = new JRadioButton("MS-DOS"));
		p1.add(m_France = new JRadioButton("France"));		
		m_Duke.setSelected(true);
		g1.add(m_Duke);
		g1.add(m_Printer);
		g1.add(m_MSDOS);		
		g1.add(m_France);
		select.add(p1, BorderLayout.WEST);		
		JPanel p2 = new JPanel();
		ButtonGroup g2 = new ButtonGroup();
		p2.setLayout(new GridLayout(0,1));		
		p2.add(m_Info = new JRadioButton("Info"));
		p2.add(m_Warning = new JRadioButton("Warning"));
		p2.add(m_Error = new JRadioButton("Error"));
		p2.add(m_None = new JRadioButton("None"));		
		m_Info.setSelected(true);
		g2.add(m_Info);
		g2.add(m_Warning);		
		g2.add(m_Error);		
		g2.add(m_None);		
		select.add(p2, BorderLayout.EAST);		
		select.add(m_Sound = new JCheckBox("Sound"), BorderLayout.NORTH);
		select.add(m_Unicode = new JCheckBox("Unicode Conversion"), BorderLayout.SOUTH);
		m_Unicode.setSelected(WindowsTrayIcon.hasUnicodeConversion(WindowsTrayIcon.UNICODE_CONV_BALLOON));
		m_Unicode.setEnabled(WindowsTrayIcon.hasUnicodeConversion(WindowsTrayIcon.UNICODE_CONV_SUPPORT));
		m_Sound.setSelected(true);
		JPanel p3 = new JPanel();
		p3.setLayout(new GridLayout(0,1));
		p3.add(new JLabel("Title:"));
		p3.add(new JLabel("Message:"));
		p3.add(new JLabel("Time:"));		
		JPanel p4 = new JPanel();
		p4.setLayout(new GridLayout(0,1));
		p4.add(m_Title = new JTextField(25));
		p4.add(m_Message = new JTextField(25));
		p4.add(m_Time = new JTextField(25));		
		m_Title.setText("WindowsTrayIcon");
		m_Message.setText("Hello World!");
		m_Time.setText("10000");
		JPanel p5 = new JPanel();
		p5.setLayout(new BorderLayout(3,3));
		p5.add(p3, BorderLayout.WEST);
		p5.add(p4, BorderLayout.EAST);		
		panel.add(p5, BorderLayout.NORTH);		
		JPanel p6 = new JPanel();
		p6.setLayout(new BorderLayout(3,3));		
		p6.add(select, BorderLayout.NORTH);		
		JPanel buttons = new JPanel();
		buttons.setLayout(new FlowLayout());
		JButton button = new JButton("Show Balloon", new ImageIcon(parent.loadImage("Balloon.gif")));
		buttons.add(button, BorderLayout.CENTER);
		p6.add(buttons, BorderLayout.SOUTH);
		panel.add(p6, BorderLayout.SOUTH);
		button.addActionListener(new BalloonListener());
		addWindowListener(new CloseWindowListener());
		panel.setBorder(new EmptyBorder(4,4,4,4));
		setContentPane(panel);
		pack();		
	}

	public void updateButtons() {
		m_Duke.setEnabled(m_Parent.getIcon(0).isVisible());
		m_Printer.setEnabled(m_Parent.getIcon(1).isVisible());
		m_MSDOS.setEnabled(m_Parent.getIcon(2).isVisible());
		m_France.setEnabled(m_Parent.getIcon(3).isVisible());
	}

	// Close listener for Show button
	private class BalloonListener implements ActionListener {

		public void actionPerformed(ActionEvent evt) {
			int icon = 0;
			int flags = 0;			
			int timeout = 10000;
			boolean unicode_cnv = false;
			if (m_Printer.isSelected()) icon = 1;
			if (m_MSDOS.isSelected())   icon = 2;
			if (m_France.isSelected())  icon = 3;
			if (m_Info.isSelected())    flags =  WindowsTrayIcon.BALLOON_INFO;
			if (m_Warning.isSelected()) flags =  WindowsTrayIcon.BALLOON_WARNING;
			if (m_Error.isSelected())   flags =  WindowsTrayIcon.BALLOON_ERROR;
			if (!m_Sound.isSelected())  flags |= WindowsTrayIcon.BALLOON_NOSOUND;
			if (m_Unicode.isSelected()) unicode_cnv = true;
			try {
				timeout = Integer.parseInt(m_Time.getText());
			} catch (NumberFormatException e) {}
			String title = m_Title.getText();
			String message = m_Message.getText();
			WindowsTrayIcon ticon = m_Parent.getIcon(icon);
			try {
				WindowsTrayIcon.enableUnicodeConversion(WindowsTrayIcon.UNICODE_CONV_BALLOON, unicode_cnv);
				ticon.showBalloon(message, title, timeout, flags);			
			} catch (TrayIconException e) {
				System.out.println("Error showing balloon message");
			}
		}
	}

	// Close listener for windows button
	private class CloseWindowListener extends WindowAdapter {

		public void windowClosing(WindowEvent evt) {
			setVisible(false);
		}
	}
}

// Panel with checkbox, string and icon
class TrayIconButton extends JPanel {

	// The checkbox for enabling this Tray Icon
	JCheckBox button;
	// The Tray Icon's class
	WindowsTrayIcon icon;
	// The Icon's image
	Image m_Image;
	// The parent window
	SwingTrayIcon m_Parent;

	// Create new panel with checkbox, string and icon
	// Name = Printer/France/Battery/MS-DOS
	// Image = 16x16 Icon gif
	// Wd = 16, Hi = 16
	public TrayIconButton(String name, Image image, int wd, int hi, SwingTrayIcon parent) 
							throws TrayIconException, InterruptedException {
		m_Parent = parent;
		setLayout(new BorderLayout(3,3));
		// Add icon image in ImageDisplayer
		add(new JLabel(new ImageIcon(image)), BorderLayout.EAST);
		// Add checkbox for enabling Tray Icon
		add(button = new JCheckBox(name), BorderLayout.CENTER);
		button.addItemListener(new ToggleListener());
		// Create Tray Icon and set tooltip
		icon = new WindowsTrayIcon(image, wd, hi);
		icon.setToolTipText(name);
		m_Image = image;
	}

	// Add Tray Icon to System Tray	
	public void selectIcon(boolean select) {
		button.setSelected(select);
	}
	
	// Add popup menu to Tray Icon	
	public void setSwingPopup(SwingTrayPopup popup) {	    
		popup.setTrayIcon(icon);
	}

	// Add popup menu to Tray Icon
	public void setPopup(TrayIconPopup popup) {
		icon.setPopup(popup);
	}

	// Add action listener to Tray Icon
	public void addActionListener(ActionListener listener) {
		icon.addActionListener(listener);
	}
	
	// Add mouse listener to Tray Icon
	public void addMouseListener(MouseListener mouse) {
		icon.addMouseListener(mouse);
	}
	
	// Add balloon listener to Tray Icon
	public void addBalloonListener(TrayBalloonListener mouse) {
		icon.addBalloonListener(mouse);
	}	

	// Icon visible?
	public boolean testVisible() {
		return icon.isVisible();
	}
	
	// Return the associated WindowsTrayIcon
	public WindowsTrayIcon getIcon() {
		return icon;
	}
	
	// Return the icon's image
	public Image getImage() {
		return m_Image;
	}

	// Listener for checkbox
	// Make icon visible/invisible depending on checkbox state
	private class ToggleListener implements ItemListener {

		public void itemStateChanged(ItemEvent evt) {
			icon.setVisible(evt.getStateChange() == ItemEvent.SELECTED);
			m_Parent.updateBalloonButtons();
		}
	}
}
